﻿using System.Collections.ObjectModel;
using System.Text;
using Lazy.Captcha.Core.Interface;

namespace Lazy.Captcha.Core.Generator.Code
{
    [WeDonekRpc.Client.Attr.IgnoreIoc]
    internal class DefaultCaptchaCodeGenerator : ICaptchaCodeGenerator
    {
        private static IReadOnlyList<char> GetCharacters ( CaptchaType captchaType )
        {
            return captchaType switch
            {
                CaptchaType.DEFAULT => Characters.DEFAULT,
                CaptchaType.CHINESE => Characters.CHINESE,
                CaptchaType.NUMBER => Characters.NUMBER,
                CaptchaType.NUMBER_ZH_CN => Characters.NUMBER_ZH_CN,
                CaptchaType.NUMBER_ZH_HK => Characters.NUMBER_ZH_HK,
                CaptchaType.WORD_NUMBER_LOWER => Characters.WORD_NUMBER_LOWER,
                CaptchaType.WORD_NUMBER_UPPER => Characters.WORD_NUMBER_UPPER,
                CaptchaType.WORD => Characters.WORD,
                CaptchaType.WORD_LOWER => Characters.WORD_LOWER,
                CaptchaType.WORD_UPPER => Characters.WORD_UPPER,

                _ => Characters.DEFAULT,
            };
        }

        private static readonly ThreadLocal<Random> ThreadRandom = new(() => new Random());
        private static Random random => ThreadRandom.Value;

        /// <summary>
        /// 中文操作符
        /// </summary>
        private static IReadOnlyDictionary<char, char> OPERATOR_MAP { get; } = new ReadOnlyDictionary<char, char>(new Dictionary<char, char>()
        {
             { '+', '加' },  { '-', '减' }
        });

        private readonly IVerCodeConfig _Config;

        internal DefaultCaptchaCodeGenerator ( IVerCodeConfig config )
        {
            this._Config = config;
        }

        /// <summary>
        /// 生成
        /// </summary>
        /// <param name="length">长度</param>
        /// <returns>（渲染文本，code）</returns>
        public (string renderText, string code) Generate ()
        {
            if ( this._Config.CaptchaType == CaptchaType.ARITHMETIC )
            {
                return this.GenerateaArithmetic(this._Config.CodeLength);
            }
            else if ( this._Config.CaptchaType == CaptchaType.ARITHMETIC_ZH )
            {
                return this.GenerateaArithmeticZh(this._Config.CodeLength);
            }
            else if ( this._Config.CaptchaType == CaptchaType.NUMBER_ZH_CN )
            {
                return this.GenerateaNumberZH(this._Config.CodeLength, false);
            }
            else if ( this._Config.CaptchaType == CaptchaType.NUMBER_ZH_HK )
            {
                return this.GenerateaNumberZH(this._Config.CodeLength, true);
            }
            else
            {
                IReadOnlyList<char> chars = GetCharacters(this._Config.CaptchaType);
                string code = this.Pick(chars, this._Config.CodeLength);
                return (code, code);
            }
        }

        private (string renderText, string code) GenerateaNumberZH ( int length, bool isHk )
        {
            StringBuilder sb1 = new StringBuilder();
            StringBuilder sb2 = new StringBuilder();
            IReadOnlyList<char> characters = isHk ? Characters.NUMBER_ZH_HK : Characters.NUMBER_ZH_CN;

            for ( int i = 0 ; i < length ; i++ )
            {
                int num = random.Next(characters.Count);
                _ = sb1.Append(characters[num]);
                _ = sb2.Append(Characters.NUMBER[num]);
            }

            return (sb1.ToString(), sb2.ToString());
        }

        /// <summary>
        /// 生成算术表达式组成部分
        /// </summary>
        /// <param name="length">数字位数</param>
        /// <returns></returns>
        private (int number1, char operators, int number2, int result) GenerateaArithmeticParts ( int length )
        {
            int max = length switch
            {
                1 => 10,
                2 => 100,
                3 => 1000,
                4 => 10000,
                5 => 100000,
                6 => 1000000,
                7 => 10000000,
                8 => 100000000,
                9 => 1000000000,
                _ => throw new ArgumentOutOfRangeException(nameof(length), "must 1 to 9")
            };

            int result = random.Next(max);
            int number1 = random.Next(max);

            if ( number1 > result )
            {
                return (number1, '-', number1 - result, result);
            }
            else
            {
                return (number1, '+', result - number1, result);
            }
        }

        /// <summary>
        /// 生成阿拉伯算术表达式
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        private (string renderText, string code) GenerateaArithmetic ( int length )
        {
            (int number1, char operators, int number2, int result) = this.GenerateaArithmeticParts(length);

            // 生成表达式
            StringBuilder sb = new StringBuilder();

            _ = sb.Append(number1).Append(operators).Append(number2); //.Append("=?"); // 显示字符数量问题，不增加最后问号

            return (sb.ToString(), result.ToString());
        }

        /// <summary>
        /// 生成汉字算术表达式
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        private (string renderText, string code) GenerateaArithmeticZh ( int length )
        {
            (string renderText, string code) = this.GenerateaArithmetic(length);
            StringBuilder sb = new StringBuilder(renderText.Length);
            foreach ( char item in renderText )
            {
                if ( item is >= '0' and <= '9' )
                {
                    _ = sb.Append(Characters.NUMBER_ZH_CN[item - '0']);
                }
                else if ( item is '+' or '-' )
                {
                    _ = sb.Append(OPERATOR_MAP[item]);
                }
                else
                {
                    _ = sb.Append(item);
                }
            }

            return (sb.ToString(), code);
        }

        /// <summary>
        /// 随机挑选字符
        /// </summary>
        /// <param name="characters">字符列表</param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        private string Pick ( IReadOnlyList<char> characters, int count )
        {
            StringBuilder result = new StringBuilder();

            for ( int i = 0 ; i < count ; i++ )
            {
                _ = result.Append(characters[random.Next(characters.Count)]);
            }

            return result.ToString();
        }
    }
}
